import base64
import codecs
import hashlib
import json
import random
import time

import execjs
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from lxml import etree


class kugou(object):
    def __init__(self, tid: str):
        """
        初始化
        :param tid: 歌单id
        """
        self.mid = '239526275778893399526700786998289824956'
        self.userid = '0'
        self.tid = tid
        self.data_list = []  # 组装歌单列表
        self.ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36'

    def kugou_signature(self, url):
        """
        计算酷狗sign
        :param url:
        :return:
        """
        uri = url.split('?')[1]
        uri_list = uri.split('&')
        ordered_list = sorted(uri_list)
        uri = 'OIlwieks28dk2k092lksi2UIkp' + "".join(ordered_list) + 'OIlwieks28dk2k092lksi2UIkp'
        return hashlib.md5(uri.encode(encoding='utf-8')).hexdigest()

    def kugou_url(self, hash):
        """
        获取酷狗音乐源地址
        :param hash:
        :return:
        """
        hax = hash + '57ae12eb6890223e355ccfcb74edf70d1005' + self.mid + self.userid
        str_md5 = hashlib.md5(hax.encode(encoding='utf-8')).hexdigest()
        url = f'https://gateway.kugou.com/i/v2/?dfid=&pid=2&mid={self.mid}&cmd=26&token=&hash={hash}&area_code=1&behavior=play&appid=1005&module=&vipType=6&ptype=1&userid=0&mtype=1&album_id=&pidversion=3001&key={str_md5}&version=10209&album_audio_id=&with_res_tag=1'
        header = {
            'Host': 'gateway.kugou.com',
            'x-router': 'tracker.kugou.com',
            'User-Agent': 'Android511-AndroidPhone-10209-14-0-NetMusic-wifi'
        }
        ret = requests.get(url, headers=header).text
        # print(ret)
        ret = ret.replace('<!--KG_TAG_RES_START-->', '')
        ret = ret.replace('<!--KG_TAG_RES_END-->', '')
        json_data = json.loads(ret)
        return json_data['url'][0]

    def kugou_lrc(self, hash):
        """
        获取酷狗歌词
        :param hash:
        :return:
        """
        url = f'http://krcs.kugou.com/search?ver=1&man=yes&client=mobi&keyword=&duration=&hash={hash}&album_audio_id='
        res = requests.get(url)
        ret = 'null'
        if res.status_code == 200:
            json_data = res.json()
            url = f'https://lyrics.kugou.com/download?ver=1&client=pc&id={json_data["candidates"][0]["id"]}&accesskey={json_data["candidates"][0]["accesskey"]}&fmt=lrc&charset=utf8'
            res = requests.get(url).json()
            ret = base64.b64decode(res['content']).decode("utf-8")
        return ret

    def kugou_list(self):
        self.data_list.clear()  # 清空列表
        """
        获取酷狗歌单列表
        :return:
        """
        url = f'http://gatewayretry.kugou.com/v2/get_other_list_file?specialid={self.tid}&need_sort=1&module=CloudMusic' \
              '&clientver=11239&pagesize=300&specalidpgc' \
              f'={self.tid}&userid=0&page=1&type=0&area_code=1&appid=1005'

        header = {
            'User-Agent': 'Android9-AndroidPhone-11239-18-0-playlist-wifi',
            'Host': 'gatewayretry.kugou.com',
            'x-router': 'pubsongscdn.kugou.com',
            'mid': '239526275778893399526700786998289824956',  # 设备id
            'dfid': '-',
            'clienttime': str(time.time()).split('.')[0]
        }
        signature = self.kugou_signature(url)
        url = url + '&signature=' + signature
        ret = requests.get(url, headers=header)
        if ret.status_code == 200:
            json_data = ret.json()
            self.data_list.clear()
            for i in json_data['data']['info']:
                name = i['name'].split(' - ')
                song_id = i['hash']
                self.data_list.append(
                    {'title': name[1], 'author': name[0],
                     'url': f'http://fs.kugou.com/{song_id}.mp3',  # 假设酷狗有类似的直接链接
                     'pic': i['cover'].replace('/{size}', ''),
                     'lrc': f'http://lyrics.kugou.com/lrc/{song_id}.lrc'})  # 假设的歌词链接
        else:
            pass
        return self.data_list

    def to_16(self, key):
        while len(key) % 16 != 0:
            key += '\0'
        return str.encode(key)

    def AES_encrypt(self, text, key, iv):
        bs = AES.block_size
        pad2 = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
        encryptor = AES.new(self.to_16(key), AES.MODE_CBC, self.to_16(iv))

        pd2 = pad(str.encode(pad2(text)), 16)

        encrypt_aes = encryptor.encrypt(pd2)
        encrypt_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8')
        return encrypt_text

    def RSA_encrypt(self, text, pubKey, modulus):
        text = text[::-1]
        rs = int(codecs.encode(text.encode('utf-8'), 'hex_codec'), 16) ** int(pubKey, 16) % int(modulus, 16)
        return format(rs, 'x').zfill(256)


class wyymusic(kugou):
    def __init__(self, tid: str):
        super(wyymusic, self).__init__(tid)
        get_i = execjs.compile(r"""
            function a(a) {
                var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
                for (d = 0; a > d; d += 1)
                    e = Math.random() * b.length,
                    e = Math.floor(e),
                    c += b.charAt(e);
                return c
            }
        """)
        # csrf_token、cookie和MUSIC_U需要抓包获取，如果你的账号开通了会员将支持会员歌曲
        self.csrf_token = '72b885e6b0e11228ed2024e2846a1aae'
        self.cookie = 'NMTID=00Oj2Mnqs1lxMVOd00Bpj0JuiRiPGoAAAGPyJrUew; __remember_me=true; _iuqxldmzr_=32; _ntes_nnid=df9672c02c9de59e215184db81d9ae10,1717057534927; _ntes_nuid=df9672c02c9de59e215184db81d9ae10; WEVNSM=1.0.0; WNMCID=psgnup.1718076450609.01.0; WM_TID=zMIKoVtyVwNEVEBEVROASIT%2Ffr00T%2FSF; WM_NI=yFacv8xPNbL0qaO1sIaxWnWEeqSatNyMXlUC5zarPRFzOTXU7F4HPI%2BltKgdMFZuaoDwKvn9iOnJgs5TurFs34ZpWsHFF9bCO6pqQL7OPz87nUlBNCtgrv%2BgcU2JTd4gNTM%3D; WM_NIKE=9ca17ae2e6ffcda170e2e6ee98b26e9689fcb2e945f1a88ba2c84a828e9fb1c24a929ba2bab26d94b4bb84b42af0fea7c3b92abb9aaf84f534bc908283cf3cb3bf81d2f243a3a8998fd64fa7af9d82f746bb93988dd849f7a6af93d35a86a681b5db5ff59afca9d343e9b4bcace6708f9a9696e46588a98597ed33ab91a2abe679f78cbb93c579f1ec9691d6798c9a8adae1498deac092d448f591a09bbb3dae8ba4a3cd49b8b9be97aa6da18bfeabd770f8b982b5d037e2a3; __snaker__id=4LwEi1rYPgYsIh6M; ntes_utid=tid._.sMydwHBNaipFV1EBEAfRoTBOTLP5ltP2._.0; sDeviceId=YD-GE%2FOM5jO2tJBF0EUFQKQ8XEbCKPpgswM; gdxidpyhxdE=BTyPg10hM7YLBH9Dr%2FJhPTSiqWmp3jk9ph%2F22y%2BT93urATyq9MVn%5Cx7ugEGQfBg2Gmn2RkPGjvqieGmSi8LUYtHT7N7HWdurOD0qAwa9W%2Fujzi1qG4q53lRtavWl7vNrXVLkeM2e5pl1rw%5CO%5CaTCaHUQJQCTMtRVash%2FV4kyqXuYHW%2BK%3A1718077352800; __csrf=72b885e6b0e11228ed2024e2846a1aae; MUSIC_U=006A71EE2D05CC403016FFD2C01B1174848359712BC8D24AC439B73532EFC1F1E59D9DF029374F51F07EAA9717B7553F61EE1AF81CC1BD01B26B548902E953F4A5F76A5C2B352AEF6AC76C39F5DB98D96B9E162008EB9B10F48D3D2D9C12482B8D268CF767C85F206C6E9FE2D37BD7E314C8BDA43FE3CAC6C4A0C009FA1CFDD990143854676A8D3C9815E1542BB806DC8DA71159F407FB3C08306BBD385BC8C9E0483A22EB21E226834B4586326661B60EAEE0DEF65BD11408365CFFE5980EBA2145FC8B68E79F9DECF536B5B01A9554E66BC175767B1F6358D61BD93F1D017A04B0983C8E31C64FDEFC3F443FD193FE4BA771F7BA25A75A17F1F182178ADA611322CE4E37609057FF146F6AA5D505910AC9312739CF6144C9F76CBDB4543F16FE048D3F140A264360931B241D9B0AAE1CCF6625A568F0DA8A786F81BD01E8C9158E2901C36036CD45171650317DCA73ECC0B6E5F98F46C3BF71FE147BFC0682D35F526895129321372ADDB8D1C1584560'
        self.MUSIC_U = '006A71EE2D05CC403016FFD2C01B1174848359712BC8D24AC439B73532EFC1F1E59D9DF029374F51F07EAA9717B7553F61EE1AF81CC1BD01B26B548902E953F4A5F76A5C2B352AEF6AC76C39F5DB98D96B9E162008EB9B10F48D3D2D9C12482B8D268CF767C85F206C6E9FE2D37BD7E314C8BDA43FE3CAC6C4A0C009FA1CFDD990143854676A8D3C9815E1542BB806DC8DA71159F407FB3C08306BBD385BC8C9E0483A22EB21E226834B4586326661B60EAEE0DEF65BD11408365CFFE5980EBA2145FC8B68E79F9DECF536B5B01A9554E66BC175767B1F6358D61BD93F1D017A04B0983C8E31C64FDEFC3F443FD193FE4BA771F7BA25A75A17F1F182178ADA611322CE4E37609057FF146F6AA5D505910AC9312739CF6144C9F76CBDB4543F16FE048D3F140A264360931B241D9B0AAE1CCF6625A568F0DA8A786F81BD01E8C9158E2901C36036CD45171650317DCA73ECC0B6E5F98F46C3BF71FE147BFC0682D35F526895129321372ADDB8D1C1584560'
        self.i = get_i.call('a', 16)
        self.iv = "0102030405060708"



    def get_encSecKey(self):
        return self.RSA_encrypt(self.i, self.b, self.c)

    def wyy_discover(self, list_id: str):
        self.data_list.clear()  # 确保在开始填充数据前清空列表
        """
        获取歌单详情
        :param list_id: 列表id
        :return: 歌单中的音乐信息列表
        """
        url = "http://music.163.com/api/v6/playlist/detail"
        response = requests.post(url, data={"s": "0", "id": list_id, "n": "1000", "t": "0"},
                            headers={"User-Agent": self.ua, "Cookie": self.cookie}).json()
        ids = ''
        for i in response['playlist']['trackIds']:
            ids += str(i['id']) + ','
        self.get_music_info(ids[:-1])
        return self.data_list

    def wyy_music_list(self, t_ids):
        """
        根据歌曲id拼接成想要的歌曲列表
        :param t_ids: 歌单列表id数组
        :return:
        """
        url = f'http://music.163.com/api/song/detail/?id={t_ids}&ids=%5B{t_ids}%5D'
        ret = requests.get(url).json()
        self.data_list.clear()
        for i in ret['songs']:
            song_id = i['id']
            author = ''
            for n in i['artists']:
                author += n['name'] + '/'
            # 直接使用网易云音乐的官方图片和歌词链接
            self.data_list.append({
                'title': i['name'],
                'author': author[:-1],
                'url': f'https://music.163.com/song/media/outer/url?id={song_id}.mp3',  # 官方URL获取方式
                'pic': i['album']['picUrl'],
                'lrc': f'http://music.163.com/api/song/media?id={song_id}'  # 官方歌词URL
            })



    def wyy_url(self, music_id: str):
        """
        获取网易音乐源地址
        :param music_id: 音乐id
        :return: 歌曲下载地址
        """
        url = 'https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=' + self.csrf_token
        encText = str(
            {'ids': "[" + str(music_id) + "]", 'encodeType': 'aac', 'csrf_token': self.csrf_token, 'level': 'standard'})
        params = self.AES_encrypt(self.AES_encrypt(encText, self.g, self.iv), self.i, self.iv)
        data = {
            'params': params,
            'encSecKey': self.get_encSecKey()
        }
        headers = {
            'User-Agent': self.ua,
            'Referer': 'https://music.163.com/',
            'Content-Type': 'application/x-www-form-urlencoded',
            'Cookie': self.cookie
        }
        response = requests.post(url, headers=headers, data=data).json()
        download_url = response['data'][0]['url']
        if download_url is None:
            download_url = self.get_wyy_playurl2(music_id)
        return download_url

    def get_music_info(self, music_ids):
        """
        根据音乐ID获取音乐信息。
        :param music_ids: 音乐ID字符串, 例如 '12345,67890'
        :return: None
        """
        ids = music_ids.split(',')
        for id in ids:
            music_detail_url = f"http://music.163.com/api/song/detail/?id={id}&ids=%5B{id}%5D"
            response = requests.get(music_detail_url).json()
            music_info = response['songs'][0]  # 取第一个歌曲的信息
            self.data_list.append({
                'title': music_info['name'],
                'author': ','.join([artist['name'] for artist in music_info['artists']]),
                'url': f'https://music.163.com/song/media/outer/url?id={id}.mp3',  # 使用官方的直接播放链接
                'pic': music_info['album']['picUrl'],
                'lrc': f'http://music.163.com/api/song/media?id={id}'  # 使用官方的歌词API
            })

    def wyy_url2(self, music_id: str):
        """
        获取网易音乐源地址2
        :param m_id: 音乐id
        :return:
        """
        url = f'https://music.163.com/api/song/enhance/player/url?id={music_id}&ids=%5B{music_id}%5D&br=3200000'
        ret = requests.get(url, headers={'cookie': self.cookie}).json()
        download_url = ret['data'][0]['url']
        if download_url:
            msg = download_url
        else:
            msg = {'msg': '出现了错误，错误位置：获取音乐源'}
        return msg

    def wyy_lrc(self, m_id: str):
        """
        获取歌词
        :param m_id: 音乐id
        :return:
        """
        url = f'https://music.163.com/api/song/lyric?id={m_id}&lv=1&kv=1&tv=-1'
        ret = requests.get(url).json()
        lrc = ret['lrc']['lyric']
        if lrc == '':
            lrc = ret['klyric']['lyric']
        return lrc



class qqmusic(wyymusic):
    def __init__(self, tid: str):
        super(qqmusic, self).__init__(tid)
        self.header = {
            'User-Agent': self.ua,
            'referer': 'https://y.qq.com/'
        }

    def encrypt(self, param):
        """
        QQ音乐sign
        :param param:
        :return:
        """
        k1 = {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "A": 10, "B": 11, "C": 12,
              "D": 13, "E": 14, "F": 15}
        l1 = [212, 45, 80, 68, 195, 163, 163, 203, 157, 220, 254, 91, 204, 79, 104, 6]
        t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
        text = json.dumps(param, separators=(',', ':'))
        md5 = hashlib.md5(text.encode()).hexdigest().upper()
        t1 = ''.join([md5[i] for i in [21, 4, 9, 26, 16, 20, 27, 30]])
        t3 = ''.join([md5[i] for i in [18, 11, 3, 2, 1, 7, 6, 25]])
        ls2 = []
        for i in range(16):
            x1 = k1[md5[i * 2]]
            x2 = k1[md5[i * 2 + 1]]
            x3 = ((x1 * 16) ^ x2) ^ l1[i]
            ls2.append(x3)

        ls3 = []
        for i in range(6):
            if i == 5:
                ls3.append(t[ls2[-1] >> 2])
                ls3.append(t[(ls2[-1] & 3) << 4])
            else:
                x4 = ls2[i * 3] >> 2
                x5 = (ls2[i * 3 + 1] >> 4) ^ ((ls2[i * 3] & 3) << 4)
                x6 = (ls2[i * 3 + 2] >> 6) ^ ((ls2[i * 3 + 1] & 15) << 2)
                x7 = 63 & ls2[i * 3 + 2]
                ls3.extend(t[x4] + t[x5] + t[x6] + t[x7])

        t2 = ''.join(ls3).replace('[\\/+]', '')
        sign = 'zzb' + (t1 + t2 + t3).lower()
        return sign

    def qq_lrc(self, mid):
        """
        QQ音乐歌词接口
        :param mid: 歌曲id
        """
        url = "https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg"
        params = dict(format="json", songmid=f"{mid}")
        res = requests.get(url, params=params, headers=self.header).json()
        data = base64.b64decode(res["lyric"]).decode("utf-8")
        # print(data)
        if data == '':
            data = '纯音乐，请欣赏'
        return data

    def get_music_list(self):
        """
        获取歌单列表
        :return:
        """
        url = 'https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0' \
              '&disstid=%s&format=jsonp&g_tk=5381&jsonpCallback=playlistinfoCallback&loginUin=0&hostUin=0&inCharset' \
              '=utf8&outCharset=utf-8?ice=0&platform=yqq&needNewCode=0' % self.tid
        ret = requests.get(url=url, headers=self.header).text
        res = json.loads(ret[21:-1])
        self.data_list.clear()
        for i in res['cdlist'][0]['songlist']:
            author = ''
            song_id = i['songmid']
            pic = 'https://y.qq.com/music/photo_new/T002R300x300M000%s.jpg' % i['albummid']
            for x in i['singer']:
                author += x['name'] + '/'
            self.data_list.append({'title': i['songname'], 'author': author[:-1],
                              'url': 'https://api2.52jan.com/qqmusic/%s' % song_id,
                              'pic': ''.join(pic),
                              'lrc': 'https://api2.52jan.com/qqmusic/lrc/%s.lrc' % song_id})
        # print(json.dumps(self.data_list))
        return self.data_list

    def get_random(self, len):
        return ''.join(str(random.choice(range(10))) for _ in range(len))

    def get_music_vkey(self, mid):
        """
        获取歌曲播放地址
        :param mid: 001GLG5B45uLhI
        :return:
        """
        i = 1
        req = 'req_' + str(i)
        # guid会影响成功率，所以要随机
        while True:
            param = {"comm": {"cv": 4747474, "ct": 24, "format": "json", "inCharset": "utf-8", "outCharset": "utf-8",
                              "notice": 0, "platform": "yqq.json", "needNewCode": 1, "uin": 838210720,
                              "g_tk_new_20200303": 744448821, "g_tk": 744448821},
                     req: {"module": "vkey.GetVkeyServer", "method": "CgiGetVkey",
                           "param": {"guid": "794" + self.get_random(7), "songmid": [mid], "songtype": [0],
                                     "uin": "838210720",
                                     "loginflag": 1, "platform": "20"}}}
            url = f'https://u.y.qq.com/cgi-bin/musics.fcg?_={round(time.time() * 1000)}&sign={self.encrypt(param)}'
            self.header[
                'cookie'] = "qq_domain_video_guid_verify=0572ccfe8fb71d4c; pgv_pvid=7514459580; _qimei_q36=; _qimei_h38=fded156bd15c06d8362ab92c02000005417b17; ptui_loginuin=2632740407; RK=gF39lAty7k; ptcz=4e0c227ec1180b8dc77feddbf4865128159cc539108aa1b235bbd21143d60dd4; fqm_pvqid=760c9673-a3b4-42ad-9ca2-7876a16dc944; ts_uid=376202578; fqm_sessionid=89851b0c-9c21-4b7d-995a-53e8401298f1; pgv_info=ssid=s670902260; ts_last=y.qq.com/; ts_refer=www.bing.com/; _qpsvr_localtk=0.08316037367972351; login_type=1; qqmusic_key=Q_H_L_63k3N1qv8cfEEedWmnOavlqVSS6wbQCSrThOkoz_yP3f3AK_LTtYGpvfbS3f2f8a94OuUmEXsaVIjULI; qm_keyst=Q_H_L_63k3N1qv8cfEEedWmnOavlqVSS6wbQCSrThOkoz_yP3f3AK_LTtYGpvfbS3f2f8a94OuUmEXsaVIjULI; psrf_qqunionid=25090D994385E4EFEC492FF14BA064FB; psrf_qqopenid=044EB479031545682D68267473F6607C; euin=ow4zoeozoKcF7n**; psrf_qqrefresh_token=12040D38D2B308F718C7374D0436AA94; psrf_qqaccess_token=EF6207E9A96B2ED5626EB412CD9C14E0; tmeLoginType=2; wxrefresh_token=; wxopenid=; wxunionid=; psrf_musickey_createtime=1718111165; psrf_access_token_expiresAt=1725887165; music_ignore_pskey=202306271436Hn@vBj; uin=2500301884"
            ret = requests.post(url=url, data=json.dumps(param, separators=(',', ':')), headers=self.header).json()
            # print(json.dumps(ret))
            code = ret['code']
            if code == 2000:
                i += 1
                req = 'req_' + str(i)
                if i > 7:
                    break
            else:
                break

        purl = ret[req]['data']['midurlinfo'][0]['purl']
        uri = random.choice(ret[req]['data']['sip']) + purl if code == 0 and purl != '' else 'vip歌曲'
        # print(uri)
        return uri


if __name__ == '__main__':
    tid = "6222311"
    musicapi = qqmusic(tid)
    music_list = musicapi.kugou_list()
    print("酷狗歌单信息：" + json.dumps(music_list))
    # 获取歌曲源地址
    # musicapi.kugou_url('0DBB2B56582BE6CB062F79D1D13FE21E')

    musicapi.tid = "7992392271"
    music_list = musicapi.wyy_discover(musicapi.tid)  # 这里传递 tid 参数
    print("网易歌单信息：" + json.dumps(music_list))

    # 获取歌曲源地址
    # musicapi.wyy_playurl('1413464902')
    # musicapi.wyy_playurl2('1413464902')

    musicapi.tid = "8463140484"
    music_list = musicapi.get_music_list()
    print("QQ歌单信息：" + json.dumps(music_list))
    # 获取歌曲源地址
    # musicapi.get_music_vkey('003XT6Ef4H6X66')